// <copyright file="Storm.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>Storm class</summary>

namespace BESASM.EMGAATS.BusinessClasses
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  /// <summary>
  /// Manages a period of rainfall
  /// </summary>
  public class Storm
  {
    private Dictionary<int, Hyetograph> hyetographs;
    private Dictionary<int, double> depthsIn;
    private IRainSource rainSource;
    private int timeStepSec;
    
    /// <summary>
    /// Initializes a new instance of the <see cref="Storm"/> class
    /// </summary>
    public Storm()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Storm"/> class
    /// </summary>
    /// <param name="aRainSource">A source of rainfall data</param>
    public Storm(IRainSource aRainSource)
    {
      this.rainSource = aRainSource;
    }

    /// <summary>
    /// Gets the dictionary of available hyetographs
    /// </summary>
    public Dictionary<int, Hyetograph> Hyetographs
    {
      get
      {
        if (this.hyetographs == null)
        {
          this.FillHyetographs();
        }

        return this.hyetographs;
      }
    }

    /// <summary>
    ///  Gets or sets the time step size in seconds
    /// </summary>
    public int TimeStepSec
    {
      get
      {
        return this.timeStepSec;
      }

      set
      {
        this.timeStepSec = value;
        if (this.depthsIn != null)
        {
          this.FillDepths();
        }
      }
    }

    /// <summary>
    /// Gets a dictionary of each raingage's depth in inches for the storm
    /// </summary>
    public Dictionary<int, double> DepthsIn
    {
      get
      {
        if (this.depthsIn == null)
        {
          this.FillDepths();
        }

        return this.depthsIn;
      }
    }

    /// <summary>
    /// Gets the list of raingauges available with the storm
    /// </summary>
    public List<int> Raingauges
    {
      get
      {
        return this.rainSource.LocationIDs.ToList();
      }
    }

    /// <summary>
    /// Returns a dictionary of nodes and their corresponding raingauges
    /// </summary>
    /// <param name="nodes">A list of nodes to evaluate</param>
    /// <returns>A dictionary of nodes and their raingauges</returns>
    public Dictionary<INode, int> NodeRaingaugeAssignments(List<INode> nodes)
    {
      Dictionary<INode, int> assignments = new Dictionary<INode, int>();

      foreach (INode n in nodes)
      {
        var hyetographDists = from h in this.rainSource.Locations
                              select new
                              {
                                LocationID = h.Key,
                                Distance = Math.Sqrt(Math.Pow(n.X - h.Value.X, 2) + Math.Pow(n.Y - h.Value.Y, 2))
                              };

        var orderedHyetographDists = (from h in hyetographDists
                                      orderby h.Distance
                                      select h).First();

        assignments.Add(n, orderedHyetographDists.LocationID);
      }

      return assignments;
    }

    /// <summary>
    /// Gets the enumeration of rainfall intensities for a given raingage
    /// </summary>
    /// <param name="raingageID">The ID of the raingage of interest</param>
    /// <returns>The enumeration of rainfall intensities corresponding to the raingage</returns>
    public IEnumerable<HyetographPoint> GetRainPoint(int raingageID)
    {
      foreach (HyetographPoint h in this.rainSource.Rainfall(raingageID))
      {
        yield return h;
      }
    }

    private void FillHyetographs()
    {
      this.hyetographs = new Dictionary<int, Hyetograph>();
      var raingages = from r in this.rainSource.Locations
                      select new { raingage = r.Key, location = r.Value };
      foreach (var r in raingages)
      {
        this.hyetographs.Add(r.raingage, new Hyetograph(r.location));
      }

      foreach (var r in raingages)
      {
        Hyetograph h = this.Hyetographs[r.raingage];
        foreach (var i in this.rainSource.Rainfall(r.raingage))
        {
          h.Data.Add(new HyetographPoint(i.DateTime, i.IntensityInPerHr));
        }
      }
    }

    private void FillDepths()
    {
      if (this.depthsIn == null)
      {
        this.depthsIn = new Dictionary<int, double>();
      }
      else
      {
        this.depthsIn.Clear();
      }

      foreach (System.Collections.Generic.KeyValuePair<int, Hyetograph> h in this.Hyetographs)
      {
        var q = (from i in h.Value.Data
                 select i.IntensityInPerHr * ((double)this.TimeStepSec / 3600.0)).Sum();
        this.depthsIn.Add(h.Key, q);
      }
    }
  }
}
